package co.edu.usbcali.ai.ga.bl;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.cgarias.utils.Random;

import co.edu.usbcali.ai.ga.Ga;
import co.edu.usbcali.ai.ga.Gene;
import co.edu.usbcali.ai.ga.Generation;
import co.edu.usbcali.ai.ga.ICromosom;
import co.edu.usbcali.ai.ga.Individual;

public class SimpleCrosser implements ICrosser {
	
	private static SimpleCrosser instance=new SimpleCrosser();
	private static Logger logger=Logger.getLogger(DetTournamentChooser.class.getClass());

	int cutType=UNIFORM_SINGLE_CUT;
	int cuts=1;
	int maxRetries=20;
	
	/* **********************************************
	 * CONSTRUCTORS
	 * **********************************************/

	/**
	 * @param cutType
	 * @param cuts
	 * @param maxRetries
	 */
	public SimpleCrosser(int cutType, int cuts, int maxRetries) {
		this.cutType = cutType;
		this.cuts = cuts;
		this.maxRetries = maxRetries;
	}

	public SimpleCrosser() {
	}

	/* **********************************************
	 * 
	 * **********************************************/


	@SuppressWarnings("unchecked")
	@Override
	public List<ICromosom> cross(ICromosom father, ICromosom mother,Class<? extends ICromosom> cromosomImplementation) throws InstantiationException, IllegalAccessException {
		int minSize;
		// Se maneja una lista de cortes porque el método puede usar multiples cortes 
		// (aunque normalmente solo es uno)
		List<Integer> fatherCuts=new ArrayList<Integer>();
		List<Integer> motherCuts=new ArrayList<Integer>();
		if (cutType==UNIFORM_SINGLE_CUT){
			// Se resta uno al tama�o de cromosoma porque son n genes y n-1 separaciones
			minSize=Math.min(mother.size(),father.size()-1);
			//Se suma siempre uno para que no se escoja el 0 ya que ser�a un corte perdido
			int cut=Random.random(minSize)+1;
			fatherCuts.add(new Integer(cut));
			motherCuts.add(new Integer(cut));
		}
		// TODO Falta colocar los otros tipos de cortes
		List<ICromosom> list=new ArrayList<ICromosom>();
		boolean starFather=true;
		Integer fatherBegin=new Integer(0);
		Integer fatherEnd=new Integer(0);
		Integer motherBegin=new Integer(0);
		Integer motherEnd=new Integer(0);
		ICromosom child1=cromosomImplementation.newInstance();
		ICromosom child2=cromosomImplementation.newInstance();

		// Esto podria coambiar para generar cromosomas de otro tipo
		child1.setType(father.getType());
		child2.setType(father.getType());
		for (int con=0;con<fatherCuts.size()+1;con++){
			fatherBegin=fatherEnd;
			List<Gene> fatherSegment;
			List<Gene> motherSegment;
			try{
				fatherEnd=(Integer)fatherCuts.get(con);
				fatherSegment=father.subCromosom(fatherBegin,fatherEnd);
			}catch (IndexOutOfBoundsException aiobe){
				fatherSegment=father.subCromosom(fatherBegin);				
			}				
			motherBegin=motherEnd;
			try{
			motherEnd=(Integer)motherCuts.get(con);
			motherSegment=mother.subCromosom(motherBegin,motherEnd);
			}catch (IndexOutOfBoundsException aiobe){
				motherSegment=mother.subCromosom(motherBegin);				
			}				
			if (starFather){
				child1.addAll(fatherSegment);
				child2.addAll(motherSegment);
			}else{
				child2.addAll(fatherSegment);
				child1.addAll(motherSegment);
			}
			starFather=!starFather;
		}

		list.add(child1);
		list.add(child2);
		return list;
	}


	public static ICrosser getInstance() {
		return instance;
	}

	@Override
	public List<Individual> cross(List<Individual> list, int children,
			int generationNumber,Class<? extends ICromosom> cromosomImplementation,Ga ga) throws InstantiationException, IllegalAccessException {
		List<ICromosom> cromosomsParentsList=Generation.getCromosomsList(list);
		List<ICromosom> cromosomsList=new ArrayList<ICromosom>(children);
		ICromosom father;
		ICromosom mother;
		for (int con=0;con<children;con+=2){
			int retries=0;
			List<ICromosom> cList;
			do{
				int fatherIndex=Random.random(list.size());
				father=cromosomsParentsList.get(fatherIndex);
				int motherIndex=Random.random(list.size());
				int ret=0;
				do{
					motherIndex=(motherIndex+1)%list.size();
					mother=cromosomsParentsList.get(motherIndex);
					logger.info("Retries mother:"+ret+","+mother+","+motherIndex);
					if (ret==list.size()){
						logger.info("father:"+father+","+fatherIndex);
						retries=maxRetries;
						break;
//						System.exit(-100);
					}
					ret++;
//				}while (motherIndex==fatherIndex);					
				}while (mother.diferentGenes(father)<2);
				cList=cross(father,mother,cromosomImplementation);
//				cList=mother.crossWith(father, cutType);
				logger.info("Retries:"+retries);
				retries++;
			}while ((cromosomsParentsList.contains(cList.get(0))||cromosomsParentsList.contains(cList.get(1))||
				cromosomsList.contains(cList.get(0))||cromosomsList.contains(cList.get(1)))&&(retries<maxRetries));
			if (retries>=maxRetries){
				logger.info("Max retries reached ");
				IMutator mutator=SimpleMutator.getInstance();
				ICromosom child0=cList.get(0);
				ICromosom child1=cList.get(1);
				while(cromosomsParentsList.contains(child0)||cromosomsList.contains(child0)){
					child0=mutator.mutate(child0,cromosomImplementation);
				}
				while(cromosomsParentsList.contains(child1)||cromosomsList.contains(child1)){
					child1=mutator.mutate(child1,cromosomImplementation);
				}
				cList.clear();
				cList.add(child0);
				cList.add(child1);
			}
			cromosomsList.addAll(cList);
		}
		return Generation.createIndividualsList(cromosomsList,generationNumber,ga);

	}

	/* **********************************************
	 * GETTERS AND SETTERS
	 * **********************************************/

	public int getCutType() {
		return cutType;
	}

	public void setCutType(int cutType) {
		this.cutType = cutType;
	}

	public int getCuts() {
		return cuts;
	}

	public void setCuts(int cuts) {
		this.cuts = cuts;
	}

	public int getMaxRetries() {
		return maxRetries;
	}

	public void setMaxRetries(int maxRetries) {
		this.maxRetries = maxRetries;
	}	
	

	
}
